/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package com.headerfooter.songhang.library.component;

import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;

import java.util.LinkedList;

import static com.headerfooter.songhang.library.component.Recycler.DIRECTION_UP;
import static com.headerfooter.songhang.library.component.Recycler.DIRECTION_DOWN;

/**
 * 自定义瀑布流布局管理器子类
 */
public class WaterfallLayoutManager extends SpanLayoutManager {
    /**
     * 构造方法
     *
     * @param spanNum 列数
     */
    public WaterfallLayoutManager(int spanNum) {
        super(spanNum);
    }

    /**
     * 设置布局方向
     *
     * @param orientation 方向
     */
    public void setOrientation(int orientation) {
        super.setOrientation(orientation);
        // 当前只实现了从上到下垂直布局
        int left = 0;
        int spanWidth = mWidthSize / mSpanNum;
        for (Span span : mSpans) {
            span.left = left;
            left += spanWidth;
        }
    }

    /**
     * 设置Recycler
     *
     * @param recycler Recycler对象
     */
    public void setRecycler(Recycler recycler) {
        mRecycler = recycler;
    }

    /**
     * 设置BaseItemProvider
     *
     * @param provider BaseItemProvider对象
     */
    public void setProvider(BaseItemProvider provider) {
        mProvider = provider;
    }

    /**
     * 填充控件
     *
     * @param left 左边
     * @param top 上边
     * @param right 右边
     * @param bottom 下边
     * @param direction 方向
     * @return boolean
     */
    public boolean fill(double left, double top, double right, double bottom, int direction) {
        int spanWidth = mWidthSize / mSpanNum;
        if (mSpans == null) {
            mSpans = new Span[mSpanNum];
            for (int i = 0; i < mSpanNum; i++) {
                Span span = new Span();
                span.index = i;
                mSpans[i] = span;
                span.left = (int) (left + i * spanWidth);
            }
        }

        LinkedList<RecyclerItem> attached = mRecycler.getAttachedScrap();
        if (attached.isEmpty()) {
            mRecycler.getAllScrap().clear();
            int count = mProvider.getCount();
            int okSpan = 0;
            for (int i = 0; i < count; i++) {
                Span span = getMinSpanWithEndLine();
                RecyclerItem item = fillChild(span.left, span.endLine, i);
                item.span = span;
                if (item.positionY >= top && item.positionY <= bottom + item.height) {//在显示区域
                    mRecycler.addItem(i, item);
                    mRecycler.attachItemToEnd(item);
                } else {
                    mRecycler.recycle(item);
                }

                span.endLine += item.height + item.marginTop + item.marginBottom;
                if (span.endLine > bottom) {
                    okSpan++;
                }
                if (okSpan >= mSpanNum) {
                    break;
                }
            }
            return true;
        } else {
            fill(left, top, right, bottom, direction, attached);
        }
        return true;
    }

    /**
     * 填充控件
     *
     * @param left 左边
     * @param top 上边
     * @param right 右边
     * @param bottom 下边
     * @param direction 方向
     * @param attached item列表
     * @return boolean
     */
    public boolean fill(double left, double top, double right, double bottom, int direction,
                        LinkedList<RecyclerItem> attached) {
        if (direction == DIRECTION_UP) {
            fillDirectionUp(left, top, right, bottom, direction, attached);
        } else if (direction == DIRECTION_DOWN) {
            fillDirectionDown(left, top, right, bottom, direction, attached);
        }
        return true;
    }

    /**
     * 向上滑动时填充控件
     *
     * @param left 左边
     * @param top 上边
     * @param right 右边
     * @param bottom 下边
     * @param direction 方向
     * @param attached item列表
     * @return boolean
     */
    public boolean fillDirectionUp(double left, double top, double right, double bottom,
                                   int direction, LinkedList<RecyclerItem> attached) {
        RecyclerItem last = attached.peekLast();
        int count = mProvider.getCount();
        if (last.index == count - 1 && last.getBottom() <= bottom) {//已经到底
            return false;
        } else {
            // 先回收
            RecyclerItem first = attached.peekFirst();
            while (first != null && first.getBottom() < top) {
                mRecycler.recycle(first);// recycle本身会remove
                first.span.startLine += first.getVSpace();
                first = attached.peekFirst();
            }

            Span minEndLineSpan = getMinSpanWithEndLine();
            Span maxEndLineSpan = getMaxSpanWithEndLine();
            int index = last.index + 1;
            while (index < count && minEndLineSpan.endLine <= bottom) {// 需要填充
                RecyclerItem item;
                if (mRecycler.getAllScrap().size() > index) {
                    item = mRecycler.getAllScrap().get(index);
                    mRecycler.recoverToEnd(item);
                } else {
                    if(index == count-1){
                        int minEnd = minEndLineSpan.endLine;
                        int maxEnd = maxEndLineSpan.endLine;
                        if(minEnd < maxEnd){
                            item = fillChild(0, maxEndLineSpan.endLine, index);
                            item.span = maxEndLineSpan;
                        } else {
                            item = fillChild(0, minEndLineSpan.endLine, index);
                            item.span = minEndLineSpan;
                        }
                    } else {
                        item = fillChild(minEndLineSpan.left, minEndLineSpan.endLine, index);
                        item.span = minEndLineSpan;
                    }
                    mRecycler.attachItemToEnd(item);
                    mRecycler.addItem(index, item);
                }
                item.span.endLine += item.getVSpace();
                minEndLineSpan = getMinSpanWithEndLine();
                maxEndLineSpan = getMaxSpanWithEndLine();
                index++;
            }
            return true;
        }
    }

    /**
     * 向下滑动时填充控件
     *
     * @param left 左边
     * @param top 上边
     * @param right 右边
     * @param bottom 下边
     * @param direction 方向
     * @param attached item列表
     * @return boolean
     */
    public boolean fillDirectionDown(double left, double top, double right, double bottom,
                                     int direction, LinkedList<RecyclerItem> attached) {
        RecyclerItem first = attached.peekFirst();
        if (first.index == 0 && first.getTop() >= top) {// 已经到顶
            return false;
        } else {
            //先回收
            RecyclerItem last = attached.peekLast();
            while (last != null && last.getTop() > bottom) {
                mRecycler.recycle(last);// recycle本身会remove
                last.span.endLine -= last.getVSpace();
                last = attached.peekFirst();
            }

            Span maxStartLineSpan = getMaxSpanWithStartLine();
            int index = first.index - 1;
            while (index >= 0 && maxStartLineSpan.startLine >= top) {// 需要填充
                RecyclerItem item = mRecycler.getAllScrap().get(index);
                if (item != null) {
                    mRecycler.recoverToStart(item);
                    item.span.startLine -= item.getVSpace();
                }
                maxStartLineSpan = getMaxSpanWithStartLine();
                index--;
            }
            return true;
        }
    }

    /**
     * 获取
     * @return Span
     */
    private Span getMinSpanWithEndLine() {
        Span result = mSpans[0];
        for (Span span : mSpans) {
            if (result == span) {
                continue;
            }
            if (result.endLine > span.endLine) {
                result = span;
            }
        }
        return result;
    }

    private Span getMaxSpanWithEndLine() {
        Span result = mSpans[0];
        for (Span span : mSpans) {
            if (result == span) {
                continue;
            }
            if (result.endLine < span.endLine) {
                result = span;
            }
        }
        return result;
    }

    private Span getMaxSpanWithStartLine() {
        Span result = mSpans[0];
        for (Span span : mSpans) {
            if (result == span) {
                continue;
            }
            if (result.startLine < span.startLine) {
                result = span;
            }
        }
        return result;
    }

    private RecyclerItem fillChild(int left, int top, int index) {
        int count = mProvider.getCount();
        int childWidth;
        if(index == 0 || index == (count - 1)){
            childWidth = mWidthSize;
        } else {
            childWidth = mWidthSize / mSpanNum;
        }
        Component child = mRecycler.getView(index, mSpanLayout);
        ComponentContainer.LayoutConfig lc = child.getLayoutConfig();
        int childWidthMeasureSpec = Component.EstimateSpec.getChildSizeWithMode(
                childWidth - lc.getMarginLeft() - lc.getMarginRight(), mWidthMode, Component.EstimateSpec.PRECISE);
        int childHeightMeasureSpec = Component.EstimateSpec.getChildSizeWithMode(
                lc.height, mHeightMode, Component.EstimateSpec.PRECISE);

        child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
        RecyclerItem item = new RecyclerItem();
        item.index = index;
        item.width = childWidth - lc.getMarginLeft() - lc.getMarginRight();
        item.height = child.getEstimatedHeight();

        item.marginLeft = lc.getMarginLeft();

        if(index > 0 && index < mSpanNum){
            item.marginTop = lc.getMarginTop() + 400 + 20;
        } else {
            item.marginTop = lc.getMarginTop();
        }

        item.marginRight = lc.getMarginRight();
        item.marginBottom = lc.getMarginBottom();

        item.positionX = left;
        item.positionY = top;
        item.child = child;

        mSpanLayout.addComponent(child);
        return item;
    }
}
